"""
文件上传工具函数
包含文件验证、存储和图片处理功能
"""
import os
import uuid
from datetime import datetime
from typing import Tuple, Optional
from fastapi import UploadFile, HTTPException, status
from PIL import Image
import aiofiles


# 上传配置
UPLOAD_DIR = "uploads"
MAX_FILE_SIZE = 30 * 1024 * 1024  # 30MB
ALLOWED_IMAGE_EXTENSIONS = {"jpg", "jpeg", "png", "gif", "webp"}
ALLOWED_DOCUMENT_EXTENSIONS = {"pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx"}

# 图片压缩配置
MAX_IMAGE_WIDTH = 1920
MAX_IMAGE_HEIGHT = 1080
THUMBNAIL_SIZE = (300, 300)


def get_file_extension(filename: str) -> str:
    """获取文件扩展名"""
    return filename.split('.')[-1].lower() if '.' in filename else ''


def is_allowed_file(filename: str, file_type: str = "image") -> bool:
    """
    检查文件类型是否允许

    Args:
        filename: 文件名
        file_type: 文件类型 ("image" 或 "document")

    Returns:
        bool: 是否允许上传
    """
    extension = get_file_extension(filename)

    if file_type == "image":
        return extension in ALLOWED_IMAGE_EXTENSIONS
    elif file_type == "document":
        return extension in ALLOWED_DOCUMENT_EXTENSIONS
    else:
        return extension in ALLOWED_IMAGE_EXTENSIONS | ALLOWED_DOCUMENT_EXTENSIONS


def generate_unique_filename(original_filename: str) -> str:
    """
    生成唯一的文件名

    格式: YYYYMMDD_HHMMSS_UUID_原始文件名

    Args:
        original_filename: 原始文件名

    Returns:
        str: 唯一文件名
    """
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    unique_id = str(uuid.uuid4())[:8]
    extension = get_file_extension(original_filename)
    base_name = original_filename.rsplit('.', 1)[0] if '.' in original_filename else original_filename

    # 清理文件名，移除特殊字符
    safe_base_name = "".join(c for c in base_name if c.isalnum() or c in (' ', '-', '_')).strip()

    return f"{timestamp}_{unique_id}_{safe_base_name}.{extension}"


def get_upload_path(file_type: str = "images") -> str:
    """
    获取上传路径

    Args:
        file_type: 文件类型目录 ("images", "documents", etc.)

    Returns:
        str: 完整的上传路径
    """
    upload_path = os.path.join(UPLOAD_DIR, file_type)
    os.makedirs(upload_path, exist_ok=True)
    return upload_path


async def validate_upload_file(file: UploadFile, file_type: str = "image") -> None:
    """
    验证上传文件

    Args:
        file: 上传的文件
        file_type: 文件类型

    Raises:
        HTTPException: 文件验证失败时抛出异常
    """
    # 检查文件名
    if not file.filename:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="文件名不能为空"
        )

    # 检查文件类型
    if not is_allowed_file(file.filename, file_type):
        allowed_extensions = ALLOWED_IMAGE_EXTENSIONS if file_type == "image" else ALLOWED_DOCUMENT_EXTENSIONS
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"不支持的文件类型。允许的扩展名: {', '.join(allowed_extensions)}"
        )

    # 检查文件大小
    if hasattr(file, 'size') and file.size and file.size > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail=f"文件大小超过限制 ({MAX_FILE_SIZE // (1024*1024)}MB)"
        )


async def save_upload_file(file: UploadFile, file_type: str = "images") -> Tuple[str, str]:
    """
    保存上传文件

    Args:
        file: 上传的文件
        file_type: 文件类型目录

    Returns:
        Tuple[str, str]: (文件路径, 文件名)
    """
    # 生成唯一文件名
    filename = generate_unique_filename(file.filename)

    # 获取保存路径
    upload_path = get_upload_path(file_type)
    file_path = os.path.join(upload_path, filename)

    # 保存文件
    async with aiofiles.open(file_path, 'wb') as f:
        content = await file.read()
        await f.write(content)

    return file_path, filename


def compress_image(image_path: str) -> Optional[Tuple[int, int]]:
    """
    压缩图片

    Args:
        image_path: 图片路径

    Returns:
        Optional[Tuple[int, int]]: 图片尺寸 (width, height)，失败时返回None
    """
    try:
        with Image.open(image_path) as img:
            # 获取原始尺寸
            original_width, original_height = img.size

            # 如果图片过大，则压缩
            if original_width > MAX_IMAGE_WIDTH or original_height > MAX_IMAGE_HEIGHT:
                # 计算压缩比例
                ratio = min(MAX_IMAGE_WIDTH / original_width, MAX_IMAGE_HEIGHT / original_height)
                new_width = int(original_width * ratio)
                new_height = int(original_height * ratio)

                # 压缩图片
                img_resized = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
                img_resized.save(image_path, optimize=True, quality=85)

                return new_width, new_height

            return original_width, original_height

    except Exception as e:
        print(f"图片压缩失败: {e}")
        return None


def create_thumbnail(image_path: str) -> Optional[str]:
    """
    创建缩略图

    Args:
        image_path: 原图路径

    Returns:
        Optional[str]: 缩略图路径，失败时返回None
    """
    try:
        # 生成缩略图路径
        base_path, ext = os.path.splitext(image_path)
        thumbnail_path = f"{base_path}_thumb{ext}"

        with Image.open(image_path) as img:
            # 创建缩略图
            img.thumbnail(THUMBNAIL_SIZE, Image.Resampling.LANCZOS)
            img.save(thumbnail_path, optimize=True, quality=85)

            return thumbnail_path

    except Exception as e:
        print(f"缩略图创建失败: {e}")
        return None